package avicit.bdp.dds.api.service;

import avicit.bdp.common.base.BaseService;
import avicit.bdp.common.utils.CheckParaUtils;
import avicit.bdp.common.utils.ConfigUtils;
import avicit.bdp.common.utils.redis.RedisCacheHelper;
import avicit.bdp.dds.api.dto.NoteBookDTO;
import avicit.bdp.dds.api.utils.ip.IpUtils;
import avicit.bdp.dds.api.utils.notebook.NoteBookExecuteUtils;
import avicit.bdp.dds.common.Constants;
import avicit.bdp.dds.dao.mapper.NoteBookDAO;
import avicit.platform6.api.system.ConvertColumnClient;
import avicit.platform6.api.system.impl.SystemConstant;
import avicit.platform6.commons.utils.BusinessUtil;
import avicit.platform6.commons.utils.ComUtil;
import avicit.platform6.core.context.ThreadContextHelper;
import avicit.platform6.core.exception.BusinessException;
import avicit.platform6.core.rest.msg.QueryRespBean;
import avicit.platform6.modules.system.syslog.service.SysLogUtil;
import com.alibaba.fastjson2.JSONObject;
import com.github.pagehelper.Page;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PostMapping;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2022-05-23
 * @类说明：NoteBookService
 * @修改记录：
 * @注意事项：
 * @主要功能：NoteBook服务服务
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class NoteBookService extends BaseService<NoteBookDAO, NoteBookDTO> {
    private static final Logger logger = LoggerFactory.getLogger(NoteBookService.class);

    /**
     * Redis中baseUrl key
     */
    private static final String ROUTE_CHANGE = "bdp:dds:gateway:change";

    @Autowired
    private ConvertColumnClient convertColumnClient;

    @Autowired
    private RedisCacheHelper redisCacheHelper;

    /**
     * 新增NoteBook服务
     * @param noteBookDTO
     * @return
     */
    public Boolean createNoteBook(NoteBookDTO noteBookDTO) {
        // step1:合法性检查
        checkCreateValidity(noteBookDTO);

        // step2:持久化到DB
        return createNoteBookInner(noteBookDTO);
    }

    /**
     * 修改NoteBook服务
     * @param noteBookDTO
     * @return
     */
    public Boolean updateNoteBook(NoteBookDTO noteBookDTO) {
        // step1:合法性检查
        checkUpdateValidity(noteBookDTO);

        // step2:持久化到DB
        return updateNoteBookInner(noteBookDTO);
    }

    /**
     * 删除Notebook服务
     * @param id 任务ID，多个id使用英文逗号隔开
     * @return
     */
    public Boolean deleteNoteBook(String id) {
        // step1:合法性检查
        checkDeleteValidity(id);

        // step2:持久化到DB
        return deleteNoteBookInner(id);
    }

    /**
     * 分页查询Notebook服务
     * @param pageNo
     * @param pageSize
     * @param name
     * @param remark
     * @param showed
     * @return
     */
    public QueryRespBean<NoteBookDTO> searchNoteBookByPage(String name, String projectId, String remark, Integer showed,
                                                   Integer pageNo, Integer pageSize) {
        QueryRespBean<NoteBookDTO> queryRespBean = new QueryRespBean<>();

        // step1:参数初始化
        pageNo = pageNo == null ? 1 : pageNo;
        pageSize = pageSize == null ? 10 : pageSize;
        NoteBookDTO queryParams = new NoteBookDTO();
        queryParams.setName(name);
        queryParams.setProjectId(projectId);
        queryParams.setRemark(remark);
        queryParams.setShowed(showed);
        queryParams.setCreatedBy(ThreadContextHelper.getUserId());

        // step2:分页查询
        com.github.pagehelper.PageHelper.startPage(pageNo, pageSize);
        Page<NoteBookDTO> queryRetList = this.mapper.searchPhmNoteBookByPage(queryParams);

        // step3:参数转换
        valueConvert(queryRetList);
        queryRespBean.setResult(queryRetList);

        return queryRespBean;
    }

    /**
     * 列举NoteBook服务列表
     * @param name
     * @param remark
     * @param showed
     * @return
     */
    @ApiOperation(value = "列举NoteBook服务列表")
    @PostMapping("/listNoteBook")
    public List<NoteBookDTO> listNoteBook(String name, String remark, Integer showed) {
        // step1:参数初始化
        NoteBookDTO queryParams = new NoteBookDTO();
        queryParams.setName(name);
        queryParams.setRemark(remark);
        queryParams.setShowed(showed);

        // step2:分页查询
        List<NoteBookDTO> queryRetList = this.mapper.searchPhmNoteBookList(queryParams);
        if (CollectionUtils.isEmpty(queryRetList)) {
            return new ArrayList<>();
        }

        // step3:参数转换
        valueConvert(queryRetList);

        return queryRetList;
    }

    /**
     * 查询NoteBook服务详情
     * @param id
     * @return
     */
    public NoteBookDTO getNoteBookDetails(String id) {
        NoteBookDTO queryRet = findNoteBookById(id);
        valueConvert(Arrays.asList(queryRet));

        return queryRet;
    }

    /**
     * 启动/停止NoteBook服务
     * @param id
     * @param commandType 0:启动；1：停止
     * @return
     */
    public Boolean execute(String id, Integer commandType) {
        // step1:合法性检查
        CheckParaUtils.checkStringValidity(id, logger, "NoteBook服务ID为空.");
        if (commandType == null || (commandType != 0 && commandType != 1)) {
            throw new BusinessException("执行命令仅支持[0,1],commandType=" + commandType);
        }

        // step2:查询旧DTO
        NoteBookDTO queryRet = findNoteBookById(id);
        if (queryRet == null) {
            throw new BusinessException("未找到NoteBook,id=%s." + id);
        }

        // step3:启动/停止NoteBook服务
        if (commandType == 0) {
            NoteBookExecuteUtils.startNoteBookSevice(queryRet);
        } else if (commandType == 1) {
            NoteBookExecuteUtils.stopNoteBookSevice(queryRet);
        }

        return true;
    }

    /**
     * 获取NoteBook启动日志
     * @param id
     * @return
     */
    public String queryLog(String id) {
        NoteBookDTO queryRet = findNoteBookById(id);
        if (queryRet == null) {
            return null;
        }

        return queryRet.getLog();
    }

    /**
     * 新增时合法性检查
     * @param noteBookDTO
     */
    private void checkCreateValidity(NoteBookDTO noteBookDTO) {
        CheckParaUtils.checkObjectValidity(noteBookDTO, logger, "参数为空.");
        CheckParaUtils.checkStringValidity(noteBookDTO.getName(), logger, "名字为空.");
    }

    /**
     * 修改时合法性检查
     * @param noteBookDTO
     */
    private void checkUpdateValidity(NoteBookDTO noteBookDTO) {
        CheckParaUtils.checkObjectValidity(noteBookDTO, logger, "参数为空.");
        CheckParaUtils.checkStringValidity(noteBookDTO.getId(), logger, "主键ID为空.");
        CheckParaUtils.checkStringValidity(noteBookDTO.getName(), logger, "名字为空.");
    }

    /**
     * 删除时合法性检查
     * @param id
     */
    private void checkDeleteValidity(String id) {
        CheckParaUtils.checkStringValidity(id, logger, "主键ID为空.");
    }

    /**
     * 新增NoteBook服务
     * @param noteBookDTO
     * @return
     */
    private Boolean createNoteBookInner(NoteBookDTO noteBookDTO) {
        // step1:参数初始化
        noteBookDTO.setId(ComUtil.getId());
        noteBookDTO.setShowed(noteBookDTO.getShowed() == null ? 1 : noteBookDTO.getShowed());
        noteBookDTO.setStatus(0);
        constructUrl(noteBookDTO);

        // step2:持久化
        this.insert(noteBookDTO);

        // step3:记录审计日志
        SysLogUtil.log4Insert(noteBookDTO);

        return true;
    }

    /**
     * 新增NoteBook服务
     * @param noteBookDTO
     * @return
     */
    private Boolean updateNoteBookInner(NoteBookDTO noteBookDTO) {
        // step1:查询旧DTO
        NoteBookDTO oldDTO = findNoteBookById(noteBookDTO.getId());
        if (oldDTO == null) {
            throw new BusinessException("未找到NoteBook,id=%s." + noteBookDTO.getId());
        }

        // step2:持久化
        this.updateByPrimaryKeySelective(noteBookDTO);

        // step3:记录审计日志
        SysLogUtil.log4Update(noteBookDTO, oldDTO);

        return true;
    }

    /**
     * 删除NoteBook
     * @param id
     * @return
     */
    private Boolean deleteNoteBookInner(String id) {
        List<String> idList = Arrays.asList(id.trim().split(Constants.COMMA));
        for (String noteBookId : idList) {
            deleteSingleNoteBook(noteBookId);
        }

        return true;
    }

    /**
     * 根据主键ID删除NoteBook
     * @param id
     */
    private void deleteSingleNoteBook(String id) {
        NoteBookDTO queryRet = findNoteBookById(id);
        if (queryRet == null) {
            return;
        }

        // 正在运行中的任务不能删除
        if (queryRet.getStatus() == 1) {
            throw new BusinessException(String.format("[%s]正在运行中，不能删除.", queryRet.getName()));
        }

        this.mapper.deleteByPrimaryKey(id);
        saveBaseUrl2Redis(queryRet);

        SysLogUtil.log4Delete(queryRet);
    }

    /**
     * 根据主键ID查询NoteBook
     * @param id
     * @return
     */
    private NoteBookDTO findNoteBookById(String id) {
        if (StringUtils.isEmpty(id)) {
            return null;
        }

        return this.selectByPrimaryKey(id);
    }

    /**
     * 获取系统中最大的NoteBook端口号
     * @return
     */
    private Integer getMaxNoteBookPort() {
        Integer maxPort = this.mapper.getPhmNoteBookMaxPort();

        return maxPort == null ? getNoteBookDefaultPort() : maxPort;
    }

    /**
     * 构建URL
     * @param noteBookDTO
     */
    private void constructUrl(NoteBookDTO noteBookDTO) {
        String localIp = IpUtils.getLocalIp4Address();
        Integer port = getMaxNoteBookPort() + 1;
        String baseUrl = "/bdp/notebook/" +  RandomStringUtils.randomAlphabetic(6).toLowerCase();
        noteBookDTO.setBaseUrl(baseUrl);// TODO:此处随机生成baseUrl可能重复
        noteBookDTO.setPort(getMaxNoteBookPort() + 1);
        noteBookDTO.setToken(ComUtil.getId());
        String url = "http://" + localIp + ":" + port + noteBookDTO.getBaseUrl() + "/" + "lab";
        noteBookDTO.setUrl(url);
    }

    /**
     * 查询结果处理
     *
     * @param noteBookDTOS
     */
    private void valueConvert(List<NoteBookDTO> noteBookDTOS) {
        if (noteBookDTOS == null || CollectionUtils.isEmpty(noteBookDTOS)) {
            return;
        }

        //循环组装请求数据
        Map<String, Set<String>> convertFormData = new HashMap<>(16);
        for (NoteBookDTO dto : noteBookDTOS) {
            if (dto == null || dto.getCreatedBy() == null) {
                return;
            }
            BusinessUtil.createConvertSet(convertFormData, SystemConstant.USER, dto.getCreatedBy());
        }
        if (convertFormData.size() > 0) {
            //获取请求结果
            Map<String, Map<String, String>> convertResultData = this.convertColumnClient.replace(convertFormData);
            //循环设置Alias或Name的值
            for (NoteBookDTO dto : noteBookDTOS) {
                if (dto == null || dto.getCreatedBy() == null) {
                    return;
                }
                dto.setCreatedBy(BusinessUtil.convertFormat(convertResultData, SystemConstant.USER, dto.getCreatedBy()));
            }
        }
    }

    /**
     * 保存baseUr到Redis
     * 注意：redis数据格式为：[{"id":"","baseUrl":"","status":"","url":""}]
     * @param noteBookDTO
     */
    public void saveBaseUrl2Redis(NoteBookDTO noteBookDTO) {
        if (noteBookDTO == null || StringUtils.isBlank(noteBookDTO.getId())) {
            return;
        }

        // 从redis中获取baseUrl列表
        List<JSONObject> objectList = new ArrayList<>();
        JSONObject newObj = new JSONObject();
        newObj.put("id", noteBookDTO.getId());
        newObj.put("baseUrl", noteBookDTO.getBaseUrl());
        newObj.put("status", noteBookDTO.getStatus());
        newObj.put("url", noteBookDTO.getUrl());
        objectList.add(newObj);

        redisCacheHelper.lpush(ROUTE_CHANGE, JSONObject.toJSONString(objectList));
    }

    /**
     * 获取notebook服务默认端口
     * @return
     */
    private Integer getNoteBookDefaultPort() {
        return ConfigUtils.getInstance().getInteger("phm.dds.notebook.port", 18888);
    }

    /**
     * 获取运行中的NoteBook列表
     * @return
     */
    public Map<Integer, NoteBookDTO> getRunningBookNoteList() {
        List<NoteBookDTO> runningNoteBookList = this.mapper.getRunningNoteBookList();
        if (CollectionUtils.isEmpty(runningNoteBookList)) {
            return new HashMap<>(16);
        }

        return runningNoteBookList
                .stream()
                .collect(Collectors.toMap(NoteBookDTO::getPid, Function.identity(), (key1, key2) -> key2));
    }
}
